import { io, Socket } from "socket.io-client";
import UserHelper from "../UserHelper";
import moment from "moment";
import ChatMessage,{ChatMessageSenderReceiverType} from "./ChatMessage";
import eventEmitter from "../../utils/EventEmitter";
import constants from "../../constants";

class ChatHelper{
    
    static socket?:Socket;
    static onError?:any;
    static onConnected?:any;
    static CurrentContactInChatDialog?:string;

    //TODO: use event emit instead callback when new msg/error coming.
    static setupSocket = (params:{onConnected?:VoidFunction,onError?:any,currentContactInChatDialog?:string})=> {
        ChatHelper.onError = params.onError;
        ChatHelper.onConnected = params.onConnected;
        ChatHelper.CurrentContactInChatDialog = params.currentContactInChatDialog;
        ChatHelper.tryConnect({onConnected:ChatHelper.onConnected,onError:ChatHelper.onError});
    }

    static sendPrivateMsg = (msg:ChatMessage,onSuccess:any,onError:any)=> {
        console.debug("private message")
        console.debug(`socket is connected : ${ChatHelper.socket?.connected}`)

        const userInfo = UserHelper.getCachedUserInfo();
        const from = userInfo.userid;
        const _sendMsg = ()=> {
            const createTime = moment().unix();
            var _msg = {
                "fromNickName": msg.fromNickName,
                "createTime": createTime,
                "message": msg.message,
                "to": msg.toUser,
                "from": from,
            };
            ChatHelper.socket?.emitWithAck("chat/private",_msg).then((res)=>{
                console.debug("send message successfully!")
                const {err,msgId} = res;
                if(!err){
                    onSuccess && onSuccess(msgId);
                }else{
                    onError && onError(err);
                }
                
            }).catch((err)=>{
                console.error(err);
                onError && onError(err);
            });
        }
        if(!ChatHelper.socket?.connected){
            ChatHelper.tryConnect({onConnected:()=>{
                _sendMsg();
            },onError:onError||ChatHelper.onError});
        }else{
            _sendMsg();
        }
    }

    static joinGroup = (groupId:string,onSuccess:any,onError:any)=>{
        const _joinGroup = ()=>{
            console.info(`start join room socket id is ${ChatHelper.socket?.id}`);
            ChatHelper.socket?.emitWithAck("chat/group/in",{groupId}).then((res:any)=>{
                console.debug("join group successfully!")
                const {err,status} = res;
                if(!err){
                    onSuccess && onSuccess(status);
                }else{
                    onError && onError(err);
                }
            }).catch((err)=>{
                console.error(err);
                onError && onError(err);
            });    
        }
        if(!ChatHelper.socket?.connected){
            ChatHelper.tryConnect({onConnected:()=>{
                _joinGroup();
            },onError:onError||ChatHelper.onError});
        }else{
            _joinGroup();
        }
    }

    static leaveGroup = (groupId:string,onSuccess:any,onError:any)=>{
       const _leaveGroup = ()=> {
            ChatHelper.socket?.emitWithAck("chat/group/leave",{groupId}).then((res)=>{
                console.debug("leave group successfully!")
                const {err,status} = res;
                if(!err){
                    onSuccess && onSuccess(status);
                }else{
                    onError && onError(err);
                }
            }).catch((err)=>{
                console.error(err);
                onError && onError(err);
            });
        }
        if(!ChatHelper.socket?.connected){
            ChatHelper.tryConnect({onConnected:()=>{
                _leaveGroup();
            },onError:onError||ChatHelper.onError});
        }else{
            _leaveGroup();
        }
    }

    static sendGroupMsg = (groupId:string,msg:ChatMessage,onSuccess:any,onError:any)=> {
        // chat/group
        const userInfo = UserHelper.getCachedUserInfo();
        const from = userInfo.userid;
        const _sendMsg = ()=> {
            // {message,to,createTime,messageId} = msgBody;
            const createTime = moment().unix();
            var _msg = {
                "fromNickName": msg.fromNickName,
                "createTime": createTime,
                "message": msg.message,
                "to": groupId,
                "from": from,
            };
            ChatHelper.socket?.emitWithAck("chat/group",_msg).then((res)=>{
                console.debug("send group message successfully!")
                const {err,msgId} = res;
                if(!err){
                    onSuccess && onSuccess(msgId);
                }else{
                    onError && onError(err);
                }
            }).catch((err)=>{
                console.error(err);
                onError && onError(err);
            });
        }
        console.debug(`socket is connected : ${ChatHelper.socket?.connected}`)
        if(!ChatHelper.socket?.connect){
            ChatHelper.tryConnect({onConnected:()=>{
                _sendMsg();
            },onError:onError||ChatHelper.onError});
        }else{
            _sendMsg();
        }
    }

    static messageFromData = (data:any)=>{
        var userInfo = UserHelper.getCachedUserInfo();

        var chatMsg = new ChatMessage({
            messageId:data.msgId,
            from:data.from,
            message:data.message,
            avatarUrl:data.avatar,
            createTime:data.createTime,
            fromNickName:data.nickname||data.username,
            toUser:userInfo.userid||"",
            messageSenderReceiverType:ChatMessageSenderReceiverType.receiver
        });
        return chatMsg
    }

    static tryConnect = (params:{onConnected?:VoidFunction,onError?:any}={onConnected:()=>{
        console.debug("socket onconnect not implementated!")
    },onError:()=>{
        console.debug("socket onerror not implementated!")
    }})=> {
        var userInfo = UserHelper.getCachedUserInfo();
        const token = userInfo.token || "";
        const auth = {
            "username": userInfo.username||"",
            "userid": userInfo.userid||"",
            "authorization": token
        }
        console.debug(`the chat server is ${process.env.REACT_APP_CHAT_ENDPOINT}`);
        const chatServer = process.env.REACT_APP_CHAT_ENDPOINT as string;
        const sk = io(chatServer,{
            reconnectionDelayMax:10000,
            // autoConnect:false,
            reconnectionAttempts:10,
            path:"/chatchannel",
            auth:auth,
            timeout:6000,
            query:{}
        }).connect();
        console.debug("socket status:",sk.connected)
                //get user token
        sk?.on("connect",()=>{
            console.debug("connect to chat server success!");
            params.onConnected && params.onConnected();
        })
        sk?.on("disconnect",(reason)=>{
            console.debug("disconnected with reason:",reason)
        })
        sk?.on("connect_error",(err)=>{
            console.debug(err.message);
            if(err.message.startsWith("errorCode:401,")){
                eventEmitter.dispatch(constants.str.network.err401);
            }
            params.onError && params.onError(err)
        })
        sk?.on("error",(err)=>{
            console.debug(err.message);
            params.onError && params.onError(err)
        })
        sk?.on("chat/sys",()=>{

        })
        sk?.on("broadcastmsg",(msg)=>{
            console.info(msg);
        })
        sk?.on("chat/private",(data:any,ack:any)=>{
            //avatar,createTime,from,message,msgId,nickname
            var chatMsg = ChatHelper.messageFromData(data);
            eventEmitter.dispatch(constants.str.chat.newMessage,chatMsg);
            eventEmitter.dispatch(constants.str.chat.newMessageNotHandle,chatMsg);
            if(typeof ack === 'function'){
                ack(["received"]);
            }
        })
        sk?.on("chat/client",()=>{

        })
        sk?.on("chat/newUser",()=>{

        })
        sk?.on("chat/group/in",()=>{
            console.debug("a user is in");
        })
        sk?.on("chat/group/leave",()=>{
            console.debug("a user is out");
        })
        sk?.on("chat/group",(data:any,ack:any)=>{
            var chatMsg = ChatHelper.messageFromData(data);
            console.debug(`received new group message ${JSON.stringify(data)}`);
            eventEmitter.dispatch(constants.str.chat.newGroupMessage,chatMsg)
            if(typeof ack === 'function'){
                ack(["received"]);
            }
        })
        sk?.on("chat/user/status",()=>{

        })
        sk.on("ping",()=>{})
        sk.on("reconnect",()=>{

        })
        sk.on("reconnect_attempt",()=>{

        })
        sk.on("reconnect_error",()=>{
            console.debug("reconnect error");
        })
        sk.on("reconnect_failed",()=>{
            console.debug("reconnect failed");
        })
        ChatHelper.socket = sk;
    }

    static dispose = ()=> {
        ChatHelper.socket?.off("ping");
        ChatHelper.socket?.off("error");
        ChatHelper.socket?.off("connect");
        ChatHelper.socket?.off("message");
        ChatHelper.socket?.off("reconnect");
        ChatHelper.socket?.off("reconnect_attempt");
        ChatHelper.socket?.off("reconnect_error");
        ChatHelper.socket?.off("reconnect_failed");
    }



}

export default ChatHelper;